
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.*;
import edu.smu.tspell.wordnet.*;

public class Generator {

  private InputDocument inputDoc;
  private double compressRatio = 0.15;
  private int blockDistance = 3;
  private String[] keywords = null;
  private String[] query = null;
  private TermCollection termCollection;
  public boolean UseSynonyms = false;
  
  // vars for positional scoring
  public boolean UsePositional = false;
  private final double posPercent = 0.2;			// top % of text gets higher scoring
  private final double posFactor = 1.1;				// top % gets scoring factor
  
  // vars for linking scoring
  public boolean UseLinking = false;
  private List<String> linkWords = new ArrayList<String>();

  public void loadFile(String inputFile) {
    inputDoc = new InputDocument();
    inputDoc.loadFile(inputFile);
  }

  public String[] getSynoyms(String word) {
    System.out.println("get syns: " + word);
    HashSet<String> set = new HashSet<String>();
    WordNetDatabase database = WordNetDatabase.getFileInstance();
    //Synset[] synsets = database.getSynsets(word, SynsetType.NOUN);
    Set<Synset> synsets = new HashSet<Synset>();
    for (SynsetType syntype : SynsetType.ALL_TYPES) {
      for(Synset synset : database.getSynsets(word, syntype)) {
        synsets.add(synset);
      }
    }

    for (Synset syn : synsets) {
      for (String s : syn.getWordForms()) {
        set.add(s);
      }
    }
    ArrayList<String> vec = new ArrayList<String>();
    for (String s : set) {
      if (s != null) {
        vec.add(s);
      }
    }
    String[] str_arr = new String[0];
    return vec.toArray(str_arr);
  }

  public void setQuery(String[] query) {
    this.query = query;

    List<String> keywordList = new ArrayList<String>();
    TermPreprocessor tp = new TermPreprocessor();
    for (String q : query) {
      for (String s : q.split(" ")) {
        String result = tp.preprocess(s);
        if (result != null) {
          System.out.println("res: " + result);
          keywordList.add(result);
          if (UseSynonyms) {
            for (String syn : getSynoyms(result)) {
              String proc_syn = tp.preprocess(syn);
              if (proc_syn != null && !keywordList.contains(proc_syn)) {
                keywordList.add(proc_syn);
              }
            }
          }
        }
      }
    }
    System.out.println("full list: " + keywordList);
    setKeywords(keywordList.toArray(new String[keywordList.size()]));
  }

  public void setKeywords(String[] keywords) {
    List<String> processedTermList = new ArrayList<String>();
    TermPreprocessor tp = new TermPreprocessor();

    String resultTerm = null;
    for (String term : keywords) {
      resultTerm = tp.preprocess(term);

      if (resultTerm != null) {
        processedTermList.add(resultTerm);
      }
    }

    this.keywords = processedTermList.toArray(new String[processedTermList.size()]);
  }

  public String generateSummary() {
    String[] significantSentences = generateSignificantSentences();
    StringBuilder builder = new StringBuilder();

    for (String sen : significantSentences) {
      builder.append(sen).append('\n');
    }

    return StringTrimmer.trim(builder.toString(), '\n');
  }

  public String generateSummaryToFile() {
    String output = generateSummary();
    try {
      PrintWriter out = new PrintWriter(new File("bios/output.txt"), "UTF-8");
      out.print(output);
      out.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
    return output;
  }

  public String[] generateSignificantSentences() {
    String[] allSentences = getAllSentences();
    double[] scores = calcAllSentenceScores();
    String[] result = null;

    List<String> significantSentences = new ArrayList<String>();
    double senThreshold = calcSentenceThreshold();

    System.out.println("sentenceThreshold = " + senThreshold);

    for (int i = 0; i < allSentences.length; i++) {
      if (scores[i] >= senThreshold) {
        significantSentences.add(allSentences[i]);

        // System.out.println(allSentences[i]);
        // System.out.println(scores[i]);

        //System.out.println("sentence[" + i + "]" + allSentences[i]);
        //System.out.println("score = " + scores[i]);
      }
    }

    result = significantSentences.toArray(new String[significantSentences.size()]);
    return result;
  }

  public double calcSentenceThreshold() {
    String[] allSentences = getAllSentences();
    float flen = Math.round(allSentences.length * compressRatio);
    int summaryLength = Math.round(flen);

    double[] scores = calcAllSentenceScores();
    Arrays.sort(scores);

    return scores[scores.length - summaryLength];
  }

  public String[] getAllSentences() {
    return inputDoc.getAllSentences();
  }

  public String[] generateSignificantTerms() {
    if (termCollection == null) {
      TermCollectionProcessor tcp = new TermCollectionProcessor();
      tcp.insertAllTerms(inputDoc.getAllTerms());
      termCollection = tcp.getTermCollection();
    }

    int termThreshold = termCollection.getFrequencyValues()[10];

    List<String> sigTerms = new ArrayList<String>();
    for (Word term : termCollection) {
      if (term.getFrequency() >= termThreshold) {
        sigTerms.add(term.getValue());
      } else {
        break;
      }
    }

    return sigTerms.toArray(new String[sigTerms.size()]);
  }

  public double[] calcAllSentenceScores() {
    String[] allSentences = getAllSentences();
    double[] scores = new double[allSentences.length];
    String[] significantTerms = generateSignificantTerms();

    int posCutoff = Math.round(Math.round(posPercent * allSentences.length));
    for (int i = 0; i < allSentences.length; i++) {
      scores[i] = calcSentenceScore(significantTerms, allSentences[i]);
      if (UsePositional) {
      	if (i <= posCutoff) {
      		scores[i] *= posFactor;
      	}
      }
      if (UseLinking) {
      	if (containsLinking(allSentences[i]) && i != 0) {
      		int max = Math.round(Math.round(Math.max(scores[i-1], scores[i])));
      		scores[i-1] = max;
      		scores[i] = max;
      	}
      }
    }

    return scores;
  }
  
  private boolean containsLinking(String sentence) {
  	if (linkWords == null || linkWords.isEmpty()) return false;
  	
  	for (int i = 0; i < linkWords.size(); i++) {
  		if (sentence.contains(linkWords.get(i))) {
  			return true;
  		}
  	}
  	
  	return false;
  }

  public double calcSentenceScore(String[] significantTerms, String sentence) {
    TextExtractor extractor = new TextExtractor();
    extractor.setText(sentence);
    String[] originalTerms = extractor.extractTerms();
    String[] processedTerms;
    List<String> processedTermList = new ArrayList<String>();

    // System.out.println("orginal Sentence:");
    for (String ot : originalTerms) {
      // System.out.print(ot+" ");
    }
    // System.out.println("");

    TermPreprocessor tp = new TermPreprocessor();
    String resultTerm = null;
    for (String term : originalTerms) {
      resultTerm = tp.preprocess(term);

      if (resultTerm != null) {
        processedTermList.add(resultTerm);
      }
    }

    processedTerms = processedTermList.toArray(new String[processedTermList.size()]);

    // System.out.print("sigTerm: ");
    for (String term : significantTerms) {
      // System.out.print(term+" ");
    }
    // System.out.println("");

    // System.out.println("processed Sentence:");
    for (String pt : processedTerms) {
      // System.out.print(pt+" ");
    }
    // System.out.println("");

    StringBuilder builder = new StringBuilder();
    for (String term : processedTerms) {
      if (significantTerms != null && term != null) {
        if (contains(term, keywords)) {
          builder.append('2');
        } else if (contains(term, significantTerms)) {
          builder.append('1');
        } else {
          builder.append('0');
        }
      }
    }

    String symbolicSentence = builder.toString();

    // System.out.println("symbolicSentence: "+symbolicSentence);

    return calcSymbolicSentenceScore(symbolicSentence);

  }

  private boolean contains(String str1, String[] strs) {
    if (strs == null) {
      return false;
    }

    for (String st : strs) {
      if (st.equals(str1)) {
        return true;
      }
    }

    return false;
  }

  private double calcSymbolicSentenceScore(String sentence) {
    String[] splits = sentence.split("000"); // block distance is 3

    for (int i = 0; i < splits.length; i++) {
      splits[i] = StringTrimmer.trim(splits[i], '0');
      // System.out.println("split["+i+"]: "+splits[i]);
    }

    double score = 0;
    double prevScore = 0;
    for (String split : splits) {
      if (split.length() != 0) {
        int sigNum = StringTrimmer.count(split, '1');
        int keywordNum = StringTrimmer.count(split, '2');

        prevScore = Math.pow((keywordNum * 2 + sigNum), 2) / split.length();
        prevScore = 0.01 * Math.pow((keywordNum * 2 + sigNum), 2)
                / split.length();
        score = Math.max(score, prevScore);
      }
    }

    return score;
  }

  public String[] getKeywords() {
    return this.keywords;
  }
  
  public void readLinkWords() {
  	String filePath = "src/linkwords.txt";
  	
  	try {
  		Scanner scanner = new Scanner(new File(filePath));
  		while (scanner.hasNextLine()) {
  			String line = scanner.nextLine().trim();
  			if (line.isEmpty()) continue;
  			linkWords.add(line);
  		}
  	} catch (Exception e) {
  		e.printStackTrace();
  	}
  }
}
